iT邦幫忙

2022 iThome 鐵人賽

DAY 5
2
Modern Web

我們可以不要 component library 了嗎?系列 第 5

day05: useElementIsScrollDown, useElementScrollPercentage

  • 分享至 

  • xImage
  •  

想讓 hooks 能夠對應 window 以外的捲動對象,今天來做點加工處理。

成品

開發思路

將原本寫死的監聽器對象改為透過 hook 參數傳進來即可。

另外在截稿前查詢到元件的 ref props 在傳入 callback function 時,該 function 的參數會是元件實例或 HTML DOM 物件(The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere.),所以也可以寫成:

import { useState, useEffect, useCallback } from 'react';
import { debounce } from 'lodash';

export default function useElementScrollPercentage<T extends Element>(
  element: T | null, // 直接傳入監聽器對象
  delay: number = 100
): number {
  /* States */
  const [percentage, setPercentage] = useState<number>(0);

  /* Functions */
  const calculatePercentage = useCallback((e: Event): void => {
    const target = e.target as Element;
    const { scrollTop, scrollHeight, clientHeight } = target;
    const percent = (scrollTop / (scrollHeight - clientHeight)) * 100;
    setPercentage(Math.round(percent));
  }, []);
  const debouncedCalcPercentage = debounce(calculatePercentage, delay);

  /* Hooks */
  useEffect(() => {
    element?.addEventListener('scroll', debouncedCalcPercentage);
    return () =>
      element?.removeEventListener('scroll', debouncedCalcPercentage);
  }, [element, debouncedCalcPercentage]);

  /* Main */
  return percentage;
}
import React, { memo, useState } from 'react';
import { css } from '@emotion/css';
import cn from 'classnames';
import SpaceWrapper from '@Components/Layout/SpaceWrapper';
import Stack from '@Components/Layout/Stack';
import useElementScrollPercentage from '@Hooks/useElementScrollPercentage';

function useElementIsScrollPercentageDemo(): React.ReactElement {
  /* States */
  const [div, setDiv] = useState<HTMLDivElement | null>(null);
  const percentage = useElementScrollPercentage(div);

  /* Main */
  return (
    <Stack>
      <SpaceWrapper padding={24}>
        <div
          className={cn(
            css({
              height: '400px',
              border: '1px solid #333',
              overflow: 'auto',
            })
          )}
          // 透過 ref callback 保存 div
          ref={(node) => setDiv(node)}
        >
          <div className={cn(css({ height: '200vh' }))} />
        </div>
      </SpaceWrapper>
      <SpaceWrapper padding={[0, 24]}>
        <React.Fragment>scroll percentage: {percentage}%</React.Fragment>
      </SpaceWrapper>
    </Stack>
  );
}

export default memo(useElementIsScrollPercentageDemo);

自評

實作上不難,但截稿前沒有實作出單一 hook 兼顧 window 與 HTML DOM 物件的捲動監聽有點可惜 (´・ω・`)

參考資料


上一篇
day04: ProgressBar
下一篇
day06: SpaceWrapper
系列文
我們可以不要 component library 了嗎?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言